package com.jy.rhin.support.aspect;

import com.jy.rhin.config.RhinWSAction;
import com.jy.rhin.support.interceptor.AuditInfoEventSender;
import com.jy.rhin.support.interceptor.SaveAuditInfoEvent;
import com.jy.rhin.support.interceptor.WsXmlThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.cxf.ext.logging.event.EventType;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @author liweiwei
 */
@Component
@Aspect
@Slf4j
public class AuditAspect {

    @Resource
    ApplicationEventPublisher eventPublisher;

    /**
     * 需要上报审计信息的接口，前置切面
     * 在进入该切面前，日志拦截器的sender {@link AuditInfoEventSender#receiveReq(String, EventType) } 已经将SOAP原始xml存入上下文
     * 当前切面将接口入参存入上下文，并触发保存审计记录的事件
     * @param joinPoint
     * @param logger
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(logger)", argNames = "joinPoint,logger")
    public Object auditBefore(ProceedingJoinPoint joinPoint, AuditLog logger) throws Throwable {
        Object arg = joinPoint.getArgs()[0];
        Object className = joinPoint.getTarget().getClass().getSimpleName();
        //从总入口进,设置入参xml
        if("RhinEntryProviderImpl".equals(className)){
            Object arg2 = joinPoint.getArgs()[1];
            WsXmlThreadLocal.setXml(arg2.toString());
        }else{
            WsXmlThreadLocal.setData(arg);
            WsXmlThreadLocal.setREQUEST_DATA(joinPoint.getArgs()[0]);
            //根据切点和入参获取事件类型
            WsXmlThreadLocal.setMETHOD(joinPoint.getTarget().getClass().getSimpleName()+"."+joinPoint.getSignature().getName());
            eventPublisher.publishEvent(new SaveAuditInfoEvent(EventType.REQ_IN));
        }
        return joinPoint.proceed();
    }

    /**
     * 需要上报审计信息的接口，后置切面
     * 当前切面将接口出参存入上下文
     * 在日志拦截器的sender {@link AuditInfoEventSender#returnResp(String, EventType)} 将会处理SOAP原始xml，存入上下文，并触发保存审计记录的事件
     * @param logger
     * @param res
     */
    @AfterReturning(value = "@annotation(logger)", argNames = "joinPoint,logger,res", returning = "res")
    public void auditAfter(JoinPoint joinPoint,AuditLog logger, Object res) {
        Object className = joinPoint.getTarget().getClass().getSimpleName();
        //总入口直直接保存审计日志
        if("RhinEntryProviderImpl".equals(className)){
            WsXmlThreadLocal.setXml(res.toString());
            if(!WsXmlThreadLocal.hasCached()){
                WsXmlThreadLocal.setREQUEST_DATA(joinPoint.getArgs()[1]);
                RhinWSAction wsAction = RhinWSAction.getByCode(joinPoint.getArgs()[0]+"");
                String cn = wsAction.get_interface().substring(wsAction.get_interface().lastIndexOf(".") + 1);
                WsXmlThreadLocal.setMETHOD(cn+"PortImpl"+"."+wsAction.getMethod());
                WsXmlThreadLocal.setData(null);
            }
            eventPublisher.publishEvent(new SaveAuditInfoEvent(EventType.RESP_OUT));
        }else{
            //非总入口进入设置基础调用参数
            WsXmlThreadLocal.setREQUEST_DATA(joinPoint.getArgs()[0]);
            WsXmlThreadLocal.setMETHOD(joinPoint.getTarget().getClass().getSimpleName()+"."+joinPoint.getSignature().getName());
            WsXmlThreadLocal.setData(res);
        }
    }

}
